
#include <cpu.h>
#include "alu.h"
#include "pentium3.h"
#include "exception.h"

/* ------------------------------------------------------------------------- */

#define calc_parity(b)		(parity_table[((b) >> 5) & 0x07] & (1 << ((b) & 0x1f)))

#define alu_inc_func(stype, name)	\
	stype fastcall alu_inc_##name(stype x) \
	{ \
		stype r = x + 1; \
		set_flag(FLAG_AF, (x & 0x0f) > 0x0e); \
		set_flag(FLAG_OF, x > 0 && r < 0); \
		set_flag(FLAG_ZF, !r); \
		set_flag(FLAG_SF, r < 0); \
		set_flag(FLAG_PF, calc_parity(r)); \
		return r; \
	}

#define alu_dec_func(stype, name)	\
	stype fastcall alu_dec_##name(stype x) \
	{ \
		stype r = x - 1; \
		set_flag(FLAG_AF, (x & 0x0f) < 0x01); \
		set_flag(FLAG_OF, x < 0 && r > 0); \
		set_flag(FLAG_ZF, !r); \
		set_flag(FLAG_SF, r < 0); \
		set_flag(FLAG_PF, calc_parity(r)); \
		return r; \
	}

#define alu_add_func(stype, utype, name)	\
	stype fastcall alu_add_##name(stype x, stype y) \
	{ \
		stype z = x + y; \
		set_flag(FLAG_CF, (utype)x > (utype)(~y)); \
		set_flag(FLAG_AF, (x & 0x0f) > (~y & 0x0f)); \
		set_flag(FLAG_OF, (x > 0 && y > 0 && z < 0) || (x < 0 && y < 0 && z > 0)); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_sub_func(stype, utype, name)	\
	stype fastcall alu_sub_##name(stype x, stype y) \
	{ \
		stype z = x - y; \
		set_flag(FLAG_CF, (utype)x < (utype)y); \
		set_flag(FLAG_AF, (x & 0x0f) < (y & 0x0f)); \
		set_flag(FLAG_OF, (x > 0 && y < 0 && z < 0) || (x < 0 && y > 0 && z > 0)); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_adc_func(stype, utype, name)	\
	stype fastcall alu_adc_##name(stype x, stype y) \
	{ \
		stype z = x + y; \
		if (get_flag(FLAG_CF)) \
			z++; \
		set_flag(FLAG_CF, (utype)x > (utype)(~y)); \
		set_flag(FLAG_AF, (x & 0x0f) > (~y & 0x0f)); \
		set_flag(FLAG_OF, (x > 0 && y > 0 && z < 0) || (x < 0 && y < 0 && z > 0)); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_sbb_func(stype, utype, name)	\
	stype fastcall alu_sbb_##name(stype x, stype y) \
	{ \
		stype z = x - y; \
		if (get_flag(FLAG_CF)) \
			z--; \
		set_flag(FLAG_CF, (utype)x < (utype)y); \
		set_flag(FLAG_AF, (x & 0x0f) < (y & 0x0f)); \
		set_flag(FLAG_OF, (x > 0 && y < 0 && z < 0) || (x < 0 && y > 0 && z > 0)); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_and_func(stype, utype, name)	\
	stype fastcall alu_and_##name(stype x, stype y) \
	{ \
		stype z = x & y; \
		set_flag(FLAG_CF, false); \
		set_flag(FLAG_OF, false); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_or_func(stype, utype, name)		\
	stype fastcall alu_or_##name(stype x, stype y) \
	{ \
		stype z = x | y; \
		set_flag(FLAG_CF, false); \
		set_flag(FLAG_OF, false); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_xor_func(stype, utype, name)	\
	stype fastcall alu_xor_##name(stype x, stype y) \
	{ \
		stype z = x ^ y; \
		set_flag(FLAG_CF, false); \
		set_flag(FLAG_OF, false); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_rol_func(stype, utype, name)	\
	stype fastcall alu_rol_##name(stype x, uint8 y) \
	{ \
		stype z; \
		y &= bitsof(x) - 1; \
		if (!y) \
			return x; \
		z = ((utype)x << y) | ((utype)x >> (bitsof(x) - y)); \
		set_flag(FLAG_CF, z & 1); \
		set_flag(FLAG_OF, (z < 0) ^ (z & 1)); \
		return z; \
	}

#define alu_ror_func(stype, utype, name)	\
	stype fastcall alu_ror_##name(stype x, uint8 y) \
	{ \
		stype z; \
		y &= bitsof(x) - 1; \
		if (!y) \
			return x; \
		z = ((utype)x >> y) | ((utype)x << (bitsof(x) - y)); \
		set_flag(FLAG_CF, z < 0); \
		set_flag(FLAG_OF, (z < 0) ^ ((z << 1) < 0)); \
		return z; \
	}

#define alu_rcl_func(stype, utype, name)	\
	stype fastcall alu_rcl_##name(stype x, uint8 y) \
	{ \
		stype z; \
		bool cf; \
		y = (y & 0x1f) % (bitsof(x) + 1); \
		if (!y) \
			return x; \
		z = ((utype)x << y) | ((utype)x >> (bitsof(x) + 1 - y)); \
		y = bitsof(x) - y; \
		cf = (y < 0) ? false : ((x & (1 << y)) != 0); \
		set_flag(FLAG_CF, cf); \
		set_flag(FLAG_OF, (z < 0) ^ cf); \
		return z; \
	}

#define alu_rcr_func(stype, utype, name)	\
	stype fastcall alu_rcr_##name(stype x, uint8 y) \
	{ \
		stype z; \
		bool cf; \
		y = (y & 0x1f) % (bitsof(x) + 1); \
		if (!y) \
			return x; \
		cf = get_flag(FLAG_CF) ? true : false; \
		z = ((utype)x >> y) | ((utype)x << (bitsof(x) + 1 - y)); \
		y--; \
		set_flag(FLAG_CF, (y > bitsof(x)) ? false : (x & (1 << y))); \
		set_flag(FLAG_OF, (x < 0) ^ cf); \
		return z; \
	}

#define alu_shl_func(stype, utype, name)	\
	stype fastcall alu_shl_##name(stype x, uint8 y) \
	{ \
		stype z; \
		bool cf; \
		y &= 0x1f; \
		if (!y) \
			return x; \
		z = (utype)x << y; \
		y = bitsof(x) - y; \
		cf = (y < 0) ? false : ((x & (1 << y)) != 0); \
		set_flag(FLAG_CF, cf); \
		set_flag(FLAG_OF, (z < 0) ^ cf); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_shr_func(stype, utype, name)	\
	stype fastcall alu_shr_##name(stype x, uint8 y) \
	{ \
		stype z; \
		y &= 0x1f; \
		if (!y) \
			return x; \
		z = (utype)x >> y; \
		y--; \
		set_flag(FLAG_CF, (y > bitsof(x)) ? false : (x & (1 << y))); \
		set_flag(FLAG_OF, x < 0); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_sar_func(stype, utype, name)	\
	stype fastcall alu_sar_##name(stype x, uint8 y) \
	{ \
		stype z; \
		y &= 0x1f; \
		if (!y) \
			return x; \
		z = x >> y; \
		y--; \
		set_flag(FLAG_CF, (y > bitsof(x)) ? (x < 0) : (x & (1 << y))); \
		set_flag(FLAG_OF, false); \
		set_flag(FLAG_ZF, !z); \
		set_flag(FLAG_SF, z < 0); \
		set_flag(FLAG_PF, calc_parity(z)); \
		return z; \
	}

#define alu_mul_func(stype, ltype, name)	\
	stype fastcall alu_mul_##name(stype x, stype y, stype *h) \
	{ \
		ltype z; \
		z = x * y; \
		*h = (stype)(z >> bitsof(x)); \
		if (*h) { \
			set_flag(FLAG_CF, true); \
			set_flag(FLAG_OF, true); \
		} else { \
			set_flag(FLAG_CF, false); \
			set_flag(FLAG_OF, false); \
		} \
		return (stype)z; \
	}

#define alu_imul_func(stype, ltype, name)	\
	stype fastcall alu_imul_##name(stype x, stype y, stype *h) \
	{ \
		ltype z; \
		z = x * y; \
		*h = (stype)(z >> bitsof(x)); \
		if (*h && *h != ~0) { \
			set_flag(FLAG_CF, true); \
			set_flag(FLAG_OF, true); \
		} else { \
			set_flag(FLAG_CF, false); \
			set_flag(FLAG_OF, false); \
		} \
		return (stype)z; \
	}

#define alu_div_func(stype, ltype, name)	\
	stype fastcall alu_div_##name(ltype x, stype y, stype *w) \
	{ \
		ltype z; \
		if (!y) \
			raise_exception(EX_DE); \
		z = x / y; \
		if (z > (stype)~0) \
			raise_exception(EX_DE); \
		*w = (stype)(x % y); \
		return (stype)z; \
	}

#define alu_idiv_func(stype, ltype, name)	\
	stype fastcall alu_idiv_##name(ltype x, stype y, stype *w) \
	{ \
		ltype z, limit; \
		if (!y) \
			raise_exception(EX_DE); \
		z = x / y; \
		limit = ~(stype)(1 << (bitsof(stype) - 1)); \
		if (z > limit || z < ((ltype)~0 & ~limit)) \
			raise_exception(EX_DE); \
		*w = (stype)(x % y); \
		return (stype)z; \
	}

#define alu_bt_func(type, name)				\
	type fastcall alu_bt_##name(type x, uint8 y) \
	{ \
		y &= bitsof(type) - 1; \
		set_flag(FLAG_CF, x & (1 << y)); \
		return x; \
	}

#define alu_bts_func(type, name)				\
	type fastcall alu_bts_##name(type x, uint8 y) \
	{ \
		type z; \
		y &= bitsof(type) - 1; \
		z = 1 << y; \
		set_flag(FLAG_CF, x & z); \
		return x | z; \
	}

#define alu_btr_func(type, name)				\
	type fastcall alu_btr_##name(type x, uint8 y) \
	{ \
		type z; \
		y &= bitsof(type) - 1; \
		z = 1 << y; \
		set_flag(FLAG_CF, x & z); \
		return x & ~z; \
	}

#define alu_btc_func(type, name)				\
	type fastcall alu_btc_##name(type x, uint8 y) \
	{ \
		bool cf; \
		type z; \
		y &= bitsof(type) - 1; \
		z = 1 << y; \
		cf = (x & z) ? true : false; \
		set_flag(FLAG_CF, cf); \
		return cf ? (x & ~z) : (x | z); \
	}

/* ------------------------------------------------------------------------- */

static uint32 parity_table[] = {
	0x96696996, 0x69969669, 0x69969669, 0x96696996,
	0x69969669, 0x96696996, 0x96696996, 0x69969669,
};

/* ------------------------------------------------------------------------- */

alu_inc_func(int8, byte)
alu_inc_func(int16, word)
alu_inc_func(int32, dword)

alu_dec_func(int8, byte)
alu_dec_func(int16, word)
alu_dec_func(int32, dword)

alu_add_func(int8, uint8, byte)
alu_add_func(int16, uint16, word)
alu_add_func(int32, uint32, dword)

alu_sub_func(int8, uint8, byte)
alu_sub_func(int16, uint16, word)
alu_sub_func(int32, uint32, dword)

alu_adc_func(int8, uint8, byte)
alu_adc_func(int16, uint16, word)
alu_adc_func(int32, uint32, dword)

alu_sbb_func(int8, uint8, byte)
alu_sbb_func(int16, uint16, word)
alu_sbb_func(int32, uint32, dword)

alu_and_func(int8, uint8, byte)
alu_and_func(int16, uint16, word)
alu_and_func(int32, uint32, dword)

alu_or_func(int8, uint8, byte)
alu_or_func(int16, uint16, word)
alu_or_func(int32, uint32, dword)

alu_xor_func(int8, uint8, byte)
alu_xor_func(int16, uint16, word)
alu_xor_func(int32, uint32, dword)

alu_rol_func(int8, uint8, byte);
alu_rol_func(int16, uint16, word);
alu_rol_func(int32, uint32, dword);

alu_ror_func(int8, uint8, byte);
alu_ror_func(int16, uint16, word);
alu_ror_func(int32, uint32, dword);

alu_rcl_func(int8, uint8, byte);
alu_rcl_func(int16, uint16, word);
alu_rcl_func(int32, uint32, dword);

alu_rcr_func(int8, uint8, byte);
alu_rcr_func(int16, uint16, word);
alu_rcr_func(int32, uint32, dword);

alu_shl_func(int8, uint8, byte);
alu_shl_func(int16, uint16, word);
alu_shl_func(int32, uint32, dword);

alu_shr_func(int8, uint8, byte);
alu_shr_func(int16, uint16, word);
alu_shr_func(int32, uint32, dword);

alu_sar_func(int8, uint8, byte);
alu_sar_func(int16, uint16, word);
alu_sar_func(int32, uint32, dword);

alu_mul_func(uint8, uint16, byte);
alu_mul_func(uint16, uint32, word);
alu_mul_func(uint32, uint64, dword);

alu_imul_func(int8, int16, byte);
alu_imul_func(int16, int32, word);
alu_imul_func(int32, int64, dword);

alu_div_func(uint8, uint16, byte);
alu_div_func(uint16, uint32, word);
alu_div_func(uint32, uint64, dword);

alu_idiv_func(int8, int16, byte);
alu_idiv_func(int16, int32, word);
alu_idiv_func(int32, int64, dword);

alu_bt_func(uint16, word);
alu_bt_func(uint32, dword);

alu_bts_func(uint16, word);
alu_bts_func(uint32, dword);

alu_btr_func(uint16, word);
alu_btr_func(uint32, dword);

alu_btc_func(uint16, word);
alu_btc_func(uint32, dword);

/* ------------------------------------------------------------------------- */
